<?php
Junction_FileCabinet::package("Junction_Db_Common");
Junction_FileCabinet::using("Junction_Db_Pdo_SqlException");
Junction_FileCabinet::using("Junction_Db_Pdo_ResultSet");
Junction_FileCabinet::using("Junction_Db_Pdo_PreparedStatement");

/**
 * PDO Database abstraction adapter
 * 
 * @author Patrick J. Mizer <patrick.mizer@gmail.com>
 * 
 * @package junction.db.pdo
 */

class Junction_Db_Pdo_Service implements Junction_Db_Common_Service
{
	/**
	 * PDO Connection instance.
	 * @var PDO
	 */
	private $_pdoConn;
	
	/**
	 * Whether or not a transaction is open.
	 * @var boolean
	 */
	private $_inTransaction;
	
	/**
	 * Number of rows affected by previous save.
	 * @var int
	 */
	private $_affectedRows;
	
	/**
	 * PDO Adapter constructor.
	 * 
	 * @param string driver name
	 * @param string hostname
	 * @param string db name
	 * @param string username
	 * @param string password
	 * @param int port
	 * @param boolean persistent connection
	 * @throws Junction_Db_Common_SqlException
	 */
    function __construct($dsn, $username = null, $password = null) 
    {
    	try {
    		$this->_pdoConn = new PDO($dsn, $username, $password);
    		$this->_transactionOpen = false;
    	} catch(PdoException $e) {
			throw new Junction_Db_Common_SqlException($e->getMessage);
    	}
    }
    
	/**
	 * Executes a SELECT SQL query
	 * 
	 * @param string SQL query
	 * @throws Junction_Db_Common_SqlException
	 * @return ResultSet
	 */
	 public function select($sql)
	 {
	 	if(strpos(trim(strtolower($sql)), 'select') !== 0){
	 		throw new Junction_Db_Common_SqlException('Only SELECT SQL may be sent to the select() method!', $sql);
	 	}
	 	
	 	$res = $this->_pdoConn->query($sql);
	 	
		if($res === false){
			if($this->_transactionOpen)
				$this->_pdoConn->rollBack();
			throw new Junction_Db_Pdo_SqlException($this->_pdoConn, $sql);
		}
		
		return new Junction_Db_Pdo_ResultSet($res, $sql);	
	 }
	 
	/**
	 * Executes a non SELECT SQL query
	 * 
	 * @param string SQL query
	 * @throws Junction_Db_Common_SqlException
	 * @return boolean
	 */	 
	 public function save($sql)
	 {
	 	if(strpos(trim(strtolower($sql)), 'select') === 0){
	 		throw new Junction_Db_Common_SqlException('SELECT SQL may not be sent to the save() method!', $sql);
	 	}
	 	
	 	$res = $this->_pdoConn->exec($sql);
	 	
		if($res === false){
			if($this->_transactionOpen)
				$this->_pdoConn->rollBack();
			throw new Junction_Db_Pdo_SqlException($this->_pdoConn, $sql);
		}
		
		$this->_affectedRows = $res;
		
		return true;	 	
	 }
	 
	/**
	 * Gets the number of rows affected by the previous query.
	 * 
	 * @return int affected rows
	 */
	public function affectedRows()
	{
		return ($this->_affectedRows === false ? 0 : $this->_affectedRows);
	}

	/**
	 * Gets the value of the last row that was inserted into a table.
	 * 
	 * @param string name of the sequence object.
	 * @return string ID
	 */	
	public function lastInsertId($name = null)
	{
		return $this->_pdoConn->lastInsertId();
	}
	
	/**
	 * Factory method which creates a PreparedStatement.
	 * 
	 * @param string SQL query.
	 * @return Junction_Db_Common_PreparedStatement.
	 */		
	public function prepare($sql)
	{
		$stmt = $this->_pdoConn->prepare(sql);
		return new Junction_Db_Pdo_PreparedStatement($stmt, $sql);
	}
	
	/**
	 * Begins an SQL transaction.
	 * 
	 * @throws Junction_Db_Common_SqlException
	 * @return boolean
	 */
	public function startTransaction()
	{
		$res = $this->_pdoConn->beginTransaction();
		
		if($res === false){
			throw new Junction_Db_Pdo_SqlException($this->_pdoConn, null);
		}
		
		$this->_transactionOpen = true;
		
		return $res;
	}
	
	/**
	 * Commits an SQL transaction.
	 * 
	 * @throws Junction_Db_Common_SqlException
	 * @return boolean
	 */
	public function stopTransaction()
	{
		$res = $this->_pdoConn->commit();
		
		if($res === false){
			throw new Junction_Db_Pdo_SqlException($this->_pdoConn, null);
		}
		
		$this->_transactionOpen = false;
		
		return $res;		
	}
}
?>